/**
 * 
 */
package com.otrace.service;

import java.util.ArrayList;
import java.util.List;

import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.MethodEntryEvent;
import com.sun.jdi.event.MethodExitEvent;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.MethodEntryRequest;
import com.sun.jdi.request.MethodExitRequest;

/**
 * @author kvn prabhu
 *
 */
public class SequenceGenerationService {
	
	public String allEventCapture(VirtualMachine vm){
		
		StringBuffer eventFlowXML = new StringBuffer("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>");
		
		
		EventRequestManager eventRequestManager = vm.eventRequestManager();
		// remove all existing events
		eventRequestManager.deleteEventRequests(eventRequestManager
				.methodEntryRequests());
		eventRequestManager.deleteEventRequests(eventRequestManager
				.methodExitRequests());
		List<String> allLoadedClasses = new ArrayList<String>();
		//get all loaded classes
		List<ReferenceType> allClasses = vm.allClasses();
		if (allClasses != null) {
			for (ReferenceType type : allClasses) {
				if (!type.name().contains("java.")
						&& !type.name().contains("javax.")
						&& !type.name().contains("sun.")
						&& !type.name().contains("com.sun.")
						&& !type.name().contains("org.")
						&& !type.name().contains("$")
						&& !type.name().contains("EDU.")
						&& !type.name().contains("bsh.")
						&& !type.name().contains("com.arjuna")
						&& !type.name().contains("gnu.")
						&& !type.name().contains("javassist.")
						&& !type.name().contains("annotation.")
						&& !type.name().contains("[]")
						&& !type.name().contains("sql")) {

					allLoadedClasses.add(type.name());

				}
			}
		}
				
		// add requests for all classes
		for(String className : allLoadedClasses){
		
		MethodEntryRequest methodEntryRequest = eventRequestManager
				.createMethodEntryRequest();
		methodEntryRequest.addClassFilter(className);
	//	methodEntryRequest.addClassFilter("com.SecondObject");
		
		methodEntryRequest.enable();
		MethodExitRequest methodExitRequest = eventRequestManager
				.createMethodExitRequest();
		methodExitRequest.addClassFilter(className);
	//	methodExitRequest.addClassFilter("com.SecondObject");
		methodExitRequest.enable();
		}
		

		EventQueue eventQueue = vm.eventQueue();
		boolean running = true;
		int callCount = 1;
		while (running) {
			EventSet eventSet = null;
			eventFlowXML.append("<flow>");
			
			try {
				if(callCount==1){
				eventSet = eventQueue.remove();
				}else{
					eventSet = eventQueue.remove(5000);
				}
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(eventSet!=null){
				eventFlowXML.append("<CallSequence>");
				eventFlowXML.append("<CallCount>");
				eventFlowXML.append(callCount);
				eventFlowXML.append("</CallCount>");
							for (Event event : eventSet) {
				if (event instanceof MethodEntryEvent) {
					processMethodEntryEvent((MethodEntryEvent) event,
							eventFlowXML);
					eventFlowXML.append("</CallSequence>");
				}
				if (event instanceof MethodExitEvent) {
					processMethodExitEvent((MethodExitEvent) event,
							eventFlowXML);
					eventFlowXML.append("</CallSequence>");
				}
			}
			
			callCount++;
			vm.resume();
			eventFlowXML.append("</flow>");
			}else{
				break;
			}

		}
		
		return eventFlowXML.toString();
	}

	private void processMethodExitEvent(MethodExitEvent event,
			StringBuffer eventFlowXML) {


		Method method = event.method();
		String methodNameString = method.name();
		ThreadReference thread = event.thread();
		try {
			List<StackFrame> stackFrames = thread.frames();
			int level = 0;
			long calleId = 0;
			String calleClass = null;
			long callerId = 0;
			String callerClass = null;
			//System.out.println("Total Frames : " + stackFrames.size());

			for (StackFrame stackFrame : stackFrames) {
				ObjectReference objectReference = stackFrame.thisObject();

				if (level == 0) {
					calleId = objectReference.uniqueID();
					calleClass = objectReference.referenceType().toString();
				}
				if (level == 1) {
					callerId = objectReference.uniqueID();
					callerClass = objectReference.referenceType().toString();
					// printDetails(objectReference, stackFrame,variablesMap);
				}

				level++;
				if (level > 1) {
					break;
				}
			}
			/*
			 * System.out.println("" + callerId + ":" + callerClass + "" +
			 * methodNameString + "" + calleId + ":" + calleClass + "" +
			 * thread.name() + "");
			 */
			eventFlowXML.append("<CallerClass>");
			eventFlowXML.append("<CallerName>");
			eventFlowXML.append(callerClass.substring(0,callerClass.indexOf('(')));
			eventFlowXML.append("</CallerName>");
			eventFlowXML.append("<MethodExit>");
			eventFlowXML.append("<MethodExitName>");
			eventFlowXML.append(methodNameString);
			eventFlowXML.append("</MethodExitName>");
			
			eventFlowXML.append("<CalleClass>");
			eventFlowXML.append("<CalleName>");
			eventFlowXML.append(calleClass.substring(0,calleClass.indexOf('(')));
			eventFlowXML.append("</CalleName>");
			eventFlowXML.append("</CalleClass>");
			eventFlowXML.append("</MethodExit>");
			eventFlowXML.append("</CallerClass>");
					

		} catch (IncompatibleThreadStateException e) {
			e.printStackTrace();
			//virtualMachine.dispose();
		}
	

	
		
	}

	private void processMethodEntryEvent(MethodEntryEvent event,
			StringBuffer eventFlowXML) {



		Method method = event.method();
		String methodNameString = method.name();
		ThreadReference thread = event.thread();
		try {
			List<StackFrame> stackFrames = thread.frames();
			int level = 0;
			long calleId = 0;
			String calleClass = null;
			long callerId = 0;
			String callerClass = null;
			//System.out.println("Total Frames : " + stackFrames.size());

			for (StackFrame stackFrame : stackFrames) {
				ObjectReference objectReference = stackFrame.thisObject();

				if (level == 0) {
					calleId = objectReference.uniqueID();
					calleClass = objectReference.referenceType().toString();
				}
				if (level == 1) {
					callerId = objectReference.uniqueID();
					callerClass = objectReference.referenceType().toString();
					// printDetails(objectReference, stackFrame,variablesMap);
				}

				level++;
				if (level > 1) {
					break;
				}
			}
			/*
			 * System.out.println("" + callerId + ":" + callerClass + "" +
			 * methodNameString + "" + calleId + ":" + calleClass + "" +
			 * thread.name() + "");
			 */
			eventFlowXML.append("<CallerClass>");
			eventFlowXML.append("<CallerName>");
			eventFlowXML.append(callerClass.substring(0,callerClass.indexOf('(')));
			eventFlowXML.append("</CallerName>");
			eventFlowXML.append("<MethodEntry>");
			eventFlowXML.append("<MethodEntryName>");
			if(methodNameString.contains("<") || methodNameString.contains(">")){
				methodNameString = methodNameString.replace("<", "");
				methodNameString = methodNameString.replace(">", "");
			}
			eventFlowXML.append(methodNameString);
			eventFlowXML.append("</MethodEntryName>");
			
			eventFlowXML.append("<CalleClass>");
			eventFlowXML.append("<CalleName>");
			eventFlowXML.append(calleClass.substring(0,calleClass.indexOf('(')));
			eventFlowXML.append("</CalleName>");
			eventFlowXML.append("</CalleClass>");
			eventFlowXML.append("</MethodEntry>");
			eventFlowXML.append("</CallerClass>");
					

		} catch (IncompatibleThreadStateException e) {
			e.printStackTrace();
			//virtualMachine.dispose();
		}
	

	
		
	
		
	}

}
